
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:image/image.dart' as img;
import 'package:liquid_pull_to_refresh/liquid_pull_to_refresh.dart' as ptr;
import 'package:material_color_utilities/material_color_utilities.dart';
import 'functional_x.dart';

import 'app_color_scheme.dart';

///消除警告-_-！
class AppStyles {}

extension ThemeX on BuildContext {
  ColorScheme get colorScheme => Theme.of(this).colorScheme;

  TextTheme get textTheme => Theme.of(this).textTheme;

  AdditionalColors? get additionalColors => Theme.of(this).extension<AdditionalColors>();

  IconThemeData get iconTheme => Theme.of(this).iconTheme;

  TextStyle? get textErrorStyle => Theme.of(this).textTheme.bodyLarge?.copyWith(color: Theme.of(this).colorScheme.error);

  TextStyle? get textBodyLargeOutlineVariant => textTheme.bodyLarge?.copyWith(color: colorScheme.outlineVariant);
  TextStyle? get textBodyMediumOutlineVariant => textTheme.bodyMedium?.copyWith(color: colorScheme.outlineVariant);
  TextStyle? get textBodySmallOutlineVariant => textTheme.bodySmall?.copyWith(color: colorScheme.outlineVariant);
}

extension TextStyleX on TextStyle {
  TextStyle withAlphaColor(int alpha, {Color? color}) {
    return copyWith(color: (color ?? this.color)?.withAlpha(alpha));
  }

  TextStyle withOpacityColor(double opacity, {Color? color}) {
    return copyWith(color: (color ?? this.color)?.withOpacity(opacity));
  }

  TextStyle withErrorColor(BuildContext context) {
    return copyWith(color: context.colorScheme.error);
  }
}

extension TextColorX on TextStyle {
  TextStyle get white => copyWith(color: Colors.white);
}

class PullToRefreshStyle extends ThemeExtension<PullToRefreshStyle> {
  final ColorScheme colorScheme;

  PullToRefreshStyle(this.colorScheme);

  @override
  ThemeExtension<PullToRefreshStyle> copyWith({ColorScheme? colorScheme}) {
    return PullToRefreshStyle(colorScheme ?? this.colorScheme);
  }

  @override
  ThemeExtension<PullToRefreshStyle> lerp(covariant ThemeExtension<PullToRefreshStyle>? other, double t) {
    if (other is! PullToRefreshStyle) {
      return this;
    }
    return PullToRefreshStyle(ColorScheme.lerp(colorScheme, other.colorScheme, t));
  }

  Widget build({
    Key? key,
    double animSpeedFactor = 1.0,
    required Widget child,
    required ptr.RefreshCallback onRefresh,
    int springAnimationDurationInMilliseconds = 1000,
    double borderWidth = 2.0,
    bool showChildOpacityTransition = true,
  }) =>
      ptr.LiquidPullToRefresh(
        key: key,
        color: colorScheme.primary.withAlpha(15),
        backgroundColor: colorScheme.primary,
        height: 80,
        springAnimationDurationInMilliseconds: springAnimationDurationInMilliseconds,
        borderWidth: borderWidth,
        showChildOpacityTransition: showChildOpacityTransition,
        animSpeedFactor: animSpeedFactor,
        onRefresh: onRefresh,
        child: child,
      );
}

class AdditionalColors extends ThemeExtension<AdditionalColors> {
  final Brightness brightness;
  AdditionalColors(this.brightness);

  ColorGroup get yellow => ColorGroup.fromHexString('ffFFB320', brightness, false);
  ColorGroup get green => ColorGroup.fromHexString('ff1ED6A6', brightness, false);
  ColorGroup get blue => ColorGroup.fromHexString('ff0AA8FF', brightness, true);
  ColorGroup get red => ColorGroup.fromHexString('ffFF3A30', brightness, true);
  ColorGroup get purple => ColorGroup.fromHexString('ff7860F6', brightness, false);

  ColorGroup get grey => Brightness.light == brightness
      ? CorePalette.of('ff00b8c4'.toColor.value).let(
          (it) => ColorGroup(
            color: Color(it.neutral.get(40)),
            onColor: Color(it.neutral.get(100)),
            containerColor: Color(it.neutralVariant.get(95)).withOpacity(0.5),
            onContainerColor: Color(it.neutralVariant.get(60)),
          ),
        )
      : CorePalette.of('ff00b8c4'.toColor.value).let(
          (it) => ColorGroup(
            color: Color(it.neutral.get(80)),
            onColor: Color(it.neutral.get(20)),
            containerColor: Color(it.neutralVariant.get(20)).withOpacity(0.5),
            onContainerColor: Color(it.neutralVariant.get(80)),
          ),
        );

  ColorGroup lightGrey(Color dartColor) => Brightness.light == brightness
      ? CorePalette.of('ffF8FAFA'.toColor.value).let(
        (it) => ColorGroup(
      color: const Color(0xffF8FAFA),
      onColor: Color(it.neutral.get(100)),
      containerColor: Color(it.neutralVariant.get(95)).withOpacity(0.5),
      onContainerColor: Color(it.neutralVariant.get(60)),
    ),
  )
      : CorePalette.of('ffF8FAFA'.toColor.value).let(
        (it) => ColorGroup(
      color: dartColor,
      onColor: Color(it.neutral.get(20)),
      containerColor: Color(it.neutralVariant.get(20)).withOpacity(0.5),
      onContainerColor: Color(it.neutralVariant.get(80)),
    ),
  );


  @override
  ThemeExtension<AdditionalColors> copyWith() {
    return this;
  }

  @override
  ThemeExtension<AdditionalColors> lerp(covariant ThemeExtension<AdditionalColors>? other, double t) {
    return this;
  }
}

class AccentColorAppBar extends ThemeExtension<AccentColorAppBar> {
  final ColorScheme colorScheme;

  AccentColorAppBar(this.colorScheme);

  AppBarTheme get data {
    final brightness = _estimateBrightnessForColor(colorScheme.primary);
    return AppBarTheme(
        backgroundColor: colorScheme.primary,
        foregroundColor: colorScheme.brightness == Brightness.light ? Colors.white : Colors.white.withOpacity(0.85),
      systemOverlayStyle: SystemUiOverlayStyle(
        statusBarColor: Colors.transparent,
        statusBarBrightness: brightness,
        statusBarIconBrightness: brightness == Brightness.light ? Brightness.dark : Brightness.light,
      ),
    );
  }

  Brightness _estimateBrightnessForColor(Color color) {
    final double relativeLuminance = color.computeLuminance();

    // See <https://www.w3.org/TR/WCAG20/#contrast-ratiodef>
    // The spec says to use kThreshold=0.0525, but Material Design appears to bias
    // more towards using light text than WCAG20 recommends. Material Design spec
    // doesn't say what value to use, but 0.15 seemed close to what the Material
    // Design spec shows for its color palette on
    // <https://material.io/go/design-theming#color-color-palette>.
    // 设计组给的主色总是偏浅，而且要求是白色文字
    const double kThreshold = 0.25;
    if ((relativeLuminance + 0.05) * (relativeLuminance + 0.05) > kThreshold) {
      return Brightness.light;
    }
    return Brightness.dark;
  }

  @override
  ThemeExtension<AccentColorAppBar> copyWith({ColorScheme? colorScheme}) {
    return AccentColorAppBar(colorScheme ?? this.colorScheme);
  }

  @override
  ThemeExtension<AccentColorAppBar> lerp(covariant ThemeExtension<AccentColorAppBar>? other, double t) {
    if (other is! AccentColorAppBar) return this;
    return AccentColorAppBar(ColorScheme.lerp(colorScheme, other.colorScheme, t));
  }
}

ColorScheme simpleColorScheme(String brandColor, bool isLight) {
  int color = int.parse(brandColor, radix: 16);
  int a = color >> (8 * 3);
  int r = color >> (8 * 2) & 0xFF;
  int g = color >> (8 * 1) & 0xFF;
  int b = color & 0xFF;
  final seedColor = Color.fromARGB(a, r, g, b);

  return ColorScheme.fromSeed(seedColor: seedColor, brightness: isLight ? Brightness.light : Brightness.dark).let((it) {
    return isLight ? it.copyWith(primary: seedColor) : it;
  });
}

class ImageScheme extends StatelessWidget {
  const ImageScheme({super.key, required this.path, required this.child});

  final String path;
  final Widget child;

  Future<ColorScheme> colorSchemeFromImage(ColorScheme base, String path) async {
    final colors = await sourceColorsFromImage(path);
    if (colors == null || colors.isEmpty) return base;
    final to = base.primary.value;
    final from = colors[0].value;
    final blended = Color(Blend.harmonize(from, to));
    final scheme = ColorScheme.fromSeed(seedColor: blended, brightness: base.brightness);
    return scheme;
  }

  Future<List<Color>?> sourceColorsFromImage(String path) async {
    try {
      final pixels = await imageToPixels(path);
      if (pixels == null || pixels.isEmpty) return null;
      final result = await QuantizerCelebi().quantize(pixels, 128);
      final ranked = Score.score(result.colorToCount);
      return ranked.map((e) => Color(e)).toList();
    } catch (e) {
      return null;
    }
  }

  Future<List<int>?> imageToPixels(String path) async {
    final data = await rootBundle.load(path);
    final image = img.PngDecoder().decode(data.buffer.asUint8List());
    if (null == image) return null;
    final bytes = image.getBytes(order: img.ChannelOrder.rgb);
    final pixels = <int>[];
    for (var i = 0; i < bytes.length; i += 3) {
      pixels.add(Color.fromARGB(255, bytes[i], bytes[i + 1], bytes[i + 2]).value);
    }
    return pixels;
  }

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    return FutureBuilder(
        future: colorSchemeFromImage(theme.colorScheme, path),
        builder: (context, snapshot) {
          final scheme = snapshot.data ?? theme.colorScheme;
          return Theme(
            data: theme.copyWith(colorScheme: scheme),
            child: child,
          );
        });
  }
}